#include "EventList.h"

#include "EventNode.h"
#include "EventString.h"

#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _(String) gettext(String)

struct _EventList
{
	EventNode parent_instance;

	euint32	  list_num;
	euint32	  data_size;
	EventType type;

	/* 尾节点 */
	EventList *end;
};

static void
event_list_finalize(EventList *self)
{
	event_node_finalize(EVENT_NODE(self));
}

EventList *
event_list_new(void)
{
	EventList *object = MALLOC_NEW(EventList);

	event_node_init(EVENT_NODE(object));
	event_object_set_finalize(object, FINALIZE(event_list_finalize));

	object->list_num  = 0;
	object->data_size = 0;
	object->type	  = EVENT_TYPE_NONE;
	object->end	  = NULL;

	return object;
}

/**
 * EventList 原始结构为：
 * byte |     1     |     4    |     4     |   n  |  1  |
 *      | EventType | list_num | data_size | data | 'L' |
 *      |   euint8  |  eint32  |   eint32  |  raw |eint8|
 *
 * EventType : EVENT_TYPE_LIST；
 *  list_num : 链表节点个数；
 * data_size : 数据块 data 的大小 n；
 *         n : 链表原始数据块，大小为 n；
 *        'L': EventList 结束标记，用于检查完整性。
 */

static int
complete_check_type(const epointer raw)
{
	EventType type = (EventType)(*((euint8 *)raw));

	if (type != EVENT_TYPE_LIST)
		return EVENT_LIST_TYPE_ERROR;
	return EVENT_LIST_SUCCESS;
}

static int
complete_check_end(const epointer raw)
{
	const eint8 *p = (eint8 *)raw;
	euint32	     size;

	if (complete_check_type(raw))
		return EVENT_LIST_TYPE_ERROR;
	p   += 5;
	size = *((euint32 *)p);
	p   += (size + 4);

	if (*p != 'L')
		return EVENT_LIST_IMCOMPLETE_ERROR;

	return EVENT_LIST_SUCCESS;
}

static int
base_data_pull(eint8 **_p, EventList **head)
{
	EventList *cur;
	EventType  type = (EventType)(**_p);
	//
	cur	  = event_list_new();
	cur->type = type;
	*_p	 += 1;
	switch (type) {
	case EVENT_TYPE_NONE:
		break;
	case EVENT_TYPE_INT8:
	case EVENT_TYPE_UINT8:
		event_node_set_data(EVENT_NODE(cur),
				    EPTR((euint64)(*((euint8 *)*_p))), NULL);
		*_p += 1;
		break;
	case EVENT_TYPE_INT16:
	case EVENT_TYPE_UINT16:
		event_node_set_data(EVENT_NODE(cur),
				    EPTR((euint64)(*((euint16 *)*_p))), NULL);
		*_p += 2;
		break;
	case EVENT_TYPE_FLOAT32:
	case EVENT_TYPE_INT32:
	case EVENT_TYPE_UINT32:
		event_node_set_data(EVENT_NODE(cur),
				    EPTR((euint64)(*((euint32 *)*_p))), NULL);
		*_p += 4;
		break;
	case EVENT_TYPE_FLOAT64:
	case EVENT_TYPE_INT64:
	case EVENT_TYPE_UINT64:
		event_node_set_data(EVENT_NODE(cur), EPTR(*((euint64 *)*_p)),
				    NULL);
		*_p += 8;
		break;
	case EVENT_TYPE_POINTER:
		event_node_set_data(EVENT_NODE(cur), (*((epointer *)*_p)),
				    NULL);
		*_p += 8;
		break;
	default:
		printf(_("Detect error type of raw epointer!\n"));
		event_object_unref(cur);
		return EVENT_LIST_PARSER_ERROR;
	}

	if (*head == NULL)
		*head = cur;
	else
		event_node_set_next(EVENT_NODE((*head)->end), EVENT_NODE(cur));
	(*head)->end = cur;

	return EVENT_LIST_RELOAD;
}

static int
struct_data_pull(eint8 **_p, EventList **head)
{
	EventList *cur;
	EventType  type = (EventType)(**_p);
	epointer   data;
	euint32	   size;
	//
	size = *((euint32 *)(*_p + 1));
	*_p += 5;
	switch (type) {
	case EVENT_TYPE_RAW_STRUCT:
	case EVENT_TYPE_RAW_DATA:
		data = event_raw_new_with_stream(size, *_p);
		break;
	case EVENT_TYPE_STRING:
		data = event_string_new_with_size(*_p, size);
		break;
	default:
		printf(_("Detect error type of raw epointer! "
			 "(struct_data_pull)\n"));
		return EVENT_LIST_PARSER_ERROR;
	}
	*_p	 += size;
	cur	  = event_list_new();
	cur->type = type;
	event_node_set_data(EVENT_NODE(cur), data, event_object_unref);
	if (*head == NULL)
		*head = cur;
	else
		event_node_set_next(EVENT_NODE((*head)->end), EVENT_NODE(cur));
	(*head)->end = cur;

	return EVENT_LIST_RELOAD;
}

EventList *
event_list_new_with_raw(const epointer raw)
{
	EventList   *head   = NULL, *tmp, *cur;
	const eint8 *p	    = (eint8 *)raw;
	int	     status = EVENT_LIST_SUCCESS;
	euint32	     list_num, size;

	return_val_if_fail(raw != NULL, event_list_new_with_raw, NULL);

	status = complete_check_end(raw);
	return_val_if_fail(status == EVENT_LIST_SUCCESS,
			   event_list_new_with_raw, NULL);

	list_num = *((euint32 *)(p + 1));
	if (list_num <= 0)
		return NULL;
	size = *((euint32 *)(p + 5));
	/* p 指向 self::data */
	p += 9;

reload:
	if (*p >= EVENT_TYPE_NONE && *p <= EVENT_TYPE_POINTER) {
		status = base_data_pull((eint8 **)&p, &head);
	} else if (*p >= EVENT_TYPE_STRING && *p <= EVENT_TYPE_RAW_DATA) {
		status = struct_data_pull((eint8 **)&p, &head);
	} else if (*p == EVENT_TYPE_LIST) {
		tmp = event_list_new_with_raw(EPTR(p));
		if (!tmp) {
			printf(_("NULL of create a new EventList Object!\n"));
			goto fatal_and_exit;
		}
		/* 此处需要刷新，tmp->data_size 才会正常 */
		event_list_refresh(tmp);
		cur	  = event_list_new();
		cur->type = EVENT_TYPE_LIST;
		event_node_set_data(EVENT_NODE(cur), tmp, event_object_unref);
		if (!head) {
			head = cur;
		} else {
			event_node_set_next(EVENT_NODE(head->end),
					    EVENT_NODE(cur));
		}
		head->end = cur;
		p	 += (10 + tmp->data_size);
		status	  = EVENT_LIST_RELOAD;
	} else if (*p == EVENT_TYPE_END) {
		status = EVENT_LIST_END_OF_DATA;
	} else {
		status = EVENT_LIST_PARSER_ERROR;
	}

	if (status == EVENT_LIST_RELOAD) {
		goto reload;
	} else if (status == EVENT_LIST_END_OF_DATA) {
		if (head) {
			head->data_size = size;
			head->list_num	= list_num;
		} else {
			head = event_list_new();
		}
		return head;
	} else if (status == EVENT_LIST_PARSER_ERROR) {
		goto fatal_and_exit;
	}

fatal_and_exit:
	event_object_unref(head);
	return NULL;
}

/**
 * 检测 EventList 大小以及条目数量，同时会更新 data_size 和 list_num
 *
 * @return 返回 EventList 序列化后的实际大小
 */
static euint32
check_list(EventList *self)
{
	EventNode	*node = EVENT_NODE(self);
	volatile euint32 sum  = 0, v;

	self->list_num = 0;
	while (node) {
		switch (EVENT_LIST(node)->type) {
		case EVENT_TYPE_INT8:
		case EVENT_TYPE_UINT8:
			v = 2;
			break;
		case EVENT_TYPE_INT16:
		case EVENT_TYPE_UINT16:
			v = 3;
			break;
		case EVENT_TYPE_INT32:
		case EVENT_TYPE_UINT32:
		case EVENT_TYPE_FLOAT32:
			v = 5;
			break;
		case EVENT_TYPE_INT64:
		case EVENT_TYPE_UINT64:
		case EVENT_TYPE_FLOAT64:
		case EVENT_TYPE_POINTER:
			v = 9;
			break;
		case EVENT_TYPE_STRING:
			v = (event_string_get_length(EVENT_STRING(node->data)) +
			     5);
			break;
		case EVENT_TYPE_RAW_STRUCT:
		case EVENT_TYPE_RAW_DATA:
			v = (event_raw_get_size(EVENT_RAW(node->data)) + 5);
			break;
		case EVENT_TYPE_LIST:
			v = check_list(EVENT_LIST(node->data));
			if (v == 0) {
				printf(_("Function 'check_list': size is 0, "
					 "type error!\n"));
				return 0;
			}
			break;
		case EVENT_TYPE_NONE:
			v = 1;
			break;
		default:
			printf(_("Function 'check_list': type error!\n"));
			return 0;
		}
		sum += v;
		if (v > 0)
			self->list_num += 1;
		node = node->next;
	}
	self->data_size = sum;
	sum	       += 10;
	return sum;
}

#define EQUALA(p, d, t)                            \
	do {                                       \
		(*(t *)(*(p))) = (*((t *)(&(d)))); \
		*(p)	      += sizeof(t);        \
	} while (0)

static void
read_list(EventList *self, epointer *p)
{
	EventNode *node = EVENT_NODE(self);
	euint32	   size;
	epointer   data;

	*((eint8 *)(*p))   = (eint8)EVENT_TYPE_LIST;
	*p		  += 1;
	*((euint32 *)(*p)) = self->list_num;
	*p		  += 4;
	*((euint32 *)(*p)) = self->data_size;
	*p		  += 4;

	while (node) {
		(*(eint8 *)(*p)) = (eint8)EVENT_LIST(node)->type;
		*p		+= 1;
		switch (EVENT_LIST(node)->type) {
		case EVENT_TYPE_INT8:
			EQUALA(p, node->data, eint8);
			break;
		case EVENT_TYPE_UINT8:
			EQUALA(p, node->data, euint8);
			break;
		case EVENT_TYPE_INT16:
			EQUALA(p, node->data, eint16);
			break;
		case EVENT_TYPE_UINT16:
			EQUALA(p, node->data, euint16);
			break;
		case EVENT_TYPE_INT32:
			EQUALA(p, node->data, eint32);
			break;
		case EVENT_TYPE_UINT32:
			EQUALA(p, node->data, euint32);
			break;
		case EVENT_TYPE_INT64:
			EQUALA(p, node->data, eint64);
			break;
		case EVENT_TYPE_UINT64:
			EQUALA(p, node->data, euint64);
			break;
		case EVENT_TYPE_FLOAT32:
			EQUALA(p, node->data, efloat32);
			break;
		case EVENT_TYPE_FLOAT64:
			EQUALA(p, node->data, efloat64);
			break;
		case EVENT_TYPE_POINTER:
			EQUALA(p, node->data, epointer);
			break;
		case EVENT_TYPE_RAW_STRUCT:
		case EVENT_TYPE_RAW_DATA:
			size = event_raw_get_size(EVENT_RAW(node->data));
			data = event_raw_get_data(EVENT_RAW(node->data));
			*((euint32 *)(*p)) = size;
			*p		  += 4;
			memcpy(*p, data, size);
			*p += size;
			break;
		case EVENT_TYPE_STRING:
			size = (euint32)event_string_get_length(
				EVENT_STRING(node->data));
			data = (epointer)ES_GET(EVENT_STRING(node->data));
			*((euint32 *)(*p)) = size;
			*p		  += 4;
			if (size > 0)
				memcpy(*p, data, size);
			*p += size;
			break;
		case EVENT_TYPE_LIST:
			*p -= 1;
			read_list(EVENT_LIST(node->data), p);
			break;
		default:
			break;
		}
		node = node->next;
	}
	(*(eint8 *)(*p)) = 'L';
	*p		+= 1;
}

#undef EQUALA

EventRaw *
event_list_export_raw(EventList *self)
{
	EventRaw *raw_object;
	euint32	  list_size;
	epointer  raw;

	return_val_if_fail(self != NULL, event_list_export_raw, NULL);

	list_size = check_list(self);
	return_val_if_fail(list_size != 0, event_list_export_raw, NULL);

	raw_object = event_raw_new(list_size);
	raw	   = event_raw_get_data(raw_object);

	read_list(self, &raw);

	return raw_object;
}

void
event_list_add_data(EventList *head, epointer data, EventType type,
		    void (*destory)(epointer))
{
	EventList *cur;
	return_if_fail(head != NULL, event_list_add_data);

	if (!head->end) {
		head->end = head;
		cur	  = head;
	} else {
		cur = event_list_new();
		event_node_set_next(EVENT_NODE(head->end), EVENT_NODE(cur));
		head->end = cur;
	}

	cur->type = type;
	event_node_set_data(EVENT_NODE(cur), data, destory);
}

void
event_list_remove_data(EventList **head_2p, epointer data)
{
	EventList *end_tmp, *current_node, *previous_node = NULL, *tmp_node;
	epointer   node_data;
	bool	   was_founded = false;
	return_if_fail(head_2p != NULL, event_list_remove_data);

	if ((*head_2p)->type == EVENT_TYPE_NONE)
		return;

	end_tmp	     = (*head_2p)->end;
	current_node = *head_2p;
	while (current_node) {
		node_data = EN_GET_D(current_node, epointer);
		if (node_data == data) {
			was_founded = true;
			break;
		}
		previous_node = current_node;
		current_node  = EN_GET_NEXT(current_node, EventList *);
	}
	if (!was_founded)
		return;
	if (*head_2p == current_node) {
		if (end_tmp == *head_2p) {
			event_object_unref(*head_2p);
			*head_2p = event_list_new();
		} else {
			tmp_node = *head_2p;
			*head_2p = EO_REF(EN_GET_NEXT(*head_2p, EventList *),
					  EventList);
			(*head_2p)->end = end_tmp;
			event_object_unref(tmp_node);
		}
	} else if (end_tmp == current_node) {
		event_node_set_next(EVENT_NODE(previous_node), NULL);
		(*head_2p)->end = previous_node;
		event_object_unref(current_node);
	} else {
		event_node_set_next(EVENT_NODE(previous_node),
				    EN_GET_NEXT(current_node, EventNode *));
		event_object_addref(EN_GET_NEXT(current_node, epointer));
		event_object_unref(current_node);
	}
}

euint32
event_list_refresh(EventList *head)
{
	return check_list(head);
}

EventType
event_list_get_node_type(EventList *this_node)
{
	return_val_if_fail(this_node != NULL, event_list_get_node_type,
			   EVENT_TYPE_NONE);
	return this_node->type;
}

bool
event_list_verify_node_type(const EventList *this_node, EventType v_type)
{
	return_val_if_fail(this_node != NULL, event_list_verify_node_type,
			   false);
	return (this_node->type == v_type);
}

static void
print_space(int num)
{
	for (int i = 0; i < num; ++i)
		printf(" ");
}

static void
list_real_print(const EventList *self, int index, int space)
{
	int		 count = 1;
	const EventList *child;
	switch (self->type) {
	case EVENT_TYPE_NONE:
		print_space(space);
		printf("%d: type<NONE>\n", index);
		break;
	case EVENT_TYPE_INT8:
		print_space(space);
		printf("%d: type<INT8>, data<%d>\n", index,
		       (int)EN_GET_D(self, eint64));
		break;
	case EVENT_TYPE_UINT8:
		print_space(space);
		printf("%d: type<UINT8>, data<%u>\n", index,
		       (euint32)EN_GET_D(self, euint64));
		break;
	case EVENT_TYPE_INT16:
		print_space(space);
		printf("%d: type<INT16>, data<%d>\n", index,
		       (int)EN_GET_D(self, eint64));
		break;
	case EVENT_TYPE_UINT16:
		print_space(space);
		printf("%d: type<UINT16>, data<%u>\n", index,
		       (euint32)EN_GET_D(self, euint64));
		break;
	case EVENT_TYPE_INT32:
		print_space(space);
		printf("%d: type<INT32>, data<%d>\n", index,
		       (int)EN_GET_D(self, eint64));
		break;
	case EVENT_TYPE_UINT32:
		print_space(space);
		printf("%d: type<UINT32>, data<%u>\n", index,
		       (euint32)EN_GET_D(self, euint64));
		break;
	case EVENT_TYPE_INT64:
		print_space(space);
		printf("%d: type<INT64>, data<%ld>\n", index,
		       EN_GET_D(self, eint64));
		break;
	case EVENT_TYPE_UINT64:
		print_space(space);
		printf("%d: type<UINT64>, data<%lu>\n", index,
		       EN_GET_D(self, euint64));
		break;
	case EVENT_TYPE_FLOAT32:
		print_space(space);
		printf("%d: type<FLOAT32>, data<%f>\n", index,
		       *((efloat32 *)(&((EventNode *)self)->data)));
		break;
	case EVENT_TYPE_FLOAT64:
		print_space(space);
		printf("%d: type<FLOAT64>, data<%lf>\n", index,
		       *((efloat64 *)(&((EventNode *)self)->data)));
		break;
	case EVENT_TYPE_POINTER:
		print_space(space);
		printf("%d: type<POINTER>, data<%p>\n", index,
		       EN_GET_D(self, epointer));
		break;
	case EVENT_TYPE_STRING:
		print_space(space);
		printf("%d: type<STRING>, data: '%s'\n", index,
		       ES_GET(EN_GET_D(self, EventString *)));
		break;
	case EVENT_TYPE_LIST:
		child = EN_GET_D(self, EventList *);
		print_space(space);
		printf(_("%d: EventList print <%p>:\n"), index, child);
		while (child) {
			list_real_print(child, count, space + 3);
			child  = EN_GET_NEXT(child, EventList *);
			count += 1;
		}
		break;
	default:
		print_space(space);
		printf(_("%d: type<%d>, data<%p>\n"), index, self->type,
		       EN_GET_D(self, epointer));
		break;
	}
}

void
event_list_print(const EventList *head)
{
	const EventList *self;
	int		 count = 1;
	printf(_("EventList print <%p>:\n"), head);
	self = head;
	while (self) {
		list_real_print(self, count, 3);
		self   = EN_GET_NEXT(self, EventList *);
		count += 1;
	}
}
